#include <memory>
#include <exception>

// These are some files that we need to include to use Ogre3D. Note that you can at the beginnings use directly "Ogre.h", to include lots of commonly used classes.
#include "OGRE/OgreRoot.h"
#include "OGRE/OgreRenderSystem.h"
#include "OGRE/OgreRenderWindow.h"
#include "OGRE/OgreWindowEventUtilities.h"
#include "OGRE/OgreManualObject.h"
#include "OGRE/OgreEntity.h"

//Here I include my other files, like the one for SimpleOgreInit...
#include "SimpleOgreInit.h"
#include "EasyDefines.h"

void AnOgreApplication()
{
	// I construct my object that will allow me to initialise Ogre easily.
	OgreEasy::SimpleOgreInit lOgreInit;

	if(!lOgreInit.initOgre())
	{
		MWARNING("Impossible to init Ogre correctly.");
		return;
	}

	Ogre::Root* lRoot = lOgreInit.mRoot.get();
	Ogre::RenderWindow* lWindow = lOgreInit.mWindow;

	Ogre::SceneManager* lScene = lRoot->createSceneManager(Ogre::ST_GENERIC, "MyFirstSceneManager");
	Ogre::SceneNode* lRootSceneNode = lScene->getRootSceneNode();
	Ogre::Camera* lCamera = lScene->createCamera("MyFirstCamera");
	Ogre::SceneNode* lCameraNode = lRootSceneNode->createChildSceneNode("MyFirstCameraNode");
	lCameraNode->attachObject(lCamera);

	float lViewportWidth = 0.88f;
	float lViewportHeight = 0.88f;
	float lViewportLeft	= (1.0f - lViewportWidth) * 0.5f;
	float lViewportTop = (1.0f - lViewportHeight) * 0.5f;
	unsigned short lMainViewportZOrder = 100;
	Ogre::Viewport * vp = lWindow->addViewport(lCamera, lMainViewportZOrder, lViewportLeft, lViewportTop, lViewportWidth, lViewportHeight);

	vp->setBackgroundColour(Ogre::ColourValue(1,0,1));

	float ratio = float(vp->getActualWidth()) / float(vp->getActualHeight());
	lCamera->setAspectRatio(ratio);
	lCamera->setNearClipDistance(1.5f);
	lCamera->setFarClipDistance(3000.0f);

	lWindow->setActive(true);
	lWindow->setAutoUpdated(false);
	lRoot->clearEventTimes();

	// Here, I create a 3D element, by using the interface of ManualObject.
	// ManualObject is very close to the opengl old simple way to specify geometry.
	// There are other interfaces (Hardwarebuffers), you can check the ogremanual fo them and wiki.
	// For each vertex I will provide positions and attributes (normal, vertex color, texture coordinates...).
	// Then for each primitive (given its type : triangle, line, line strip etc...), 
	// I give the corresponding group of vertex index.
	Ogre::ManualObject* lManualObject = NULL;

	// The manualObject creation requires a name.
	Ogre::String lManualObjectName = "CubeWithAxes";
	lManualObject = lScene->createManualObject(lManualObjectName);
 
	// Always tell if you want to update the 3D (vertex/index) later or not.
	bool lDoIWantToUpdateItLater = false;
	lManualObject->setDynamic(lDoIWantToUpdateItLater);
 
	// Here I create a cube in a first part with triangles, and then axes (in red/green/blue).
 	// BaseWhiteNoLighting is the name of a material that already exist inside Ogre.
	// Ogre::RenderOperation::OT_TRIANGLE_LIST is a kind of primitive.
	float lSize = 0.7f;

	lManualObject->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_TRIANGLE_LIST);
	float cp = 1.0f * lSize ;
	float cm = -1.0f * lSize;
 
	lManualObject->position(cm, cp, cm);// a vertex
	lManualObject->colour(Ogre::ColourValue(0.0f,1.0f,0.0f,1.0f));
	lManualObject->position(cp, cp, cm);// a vertex
	lManualObject->colour(Ogre::ColourValue(1.0f,1.0f,0.0f,1.0f));
	lManualObject->position(cp, cm, cm);// a vertex
	lManualObject->colour(Ogre::ColourValue(1.0f,0.0f,0.0f,1.0f));
	lManualObject->position(cm, cm, cm);// a vertex
	lManualObject->colour(Ogre::ColourValue(0.0f,0.0f,0.0f,1.0f));
 
	lManualObject->position(cm, cp, cp);// a vertex
	lManualObject->colour(Ogre::ColourValue(0.0f,1.0f,1.0f,1.0f));
	lManualObject->position(cp, cp, cp);// a vertex
	lManualObject->colour(Ogre::ColourValue(1.0f,1.0f,1.0f,1.0f));
	lManualObject->position(cp, cm, cp);// a vertex
	lManualObject->colour(Ogre::ColourValue(1.0f,0.0f,1.0f,1.0f));
	lManualObject->position(cm, cm, cp);// a vertex
	lManualObject->colour(Ogre::ColourValue(0.0f,0.0f,1.0f,1.0f));
 
	// face behind / front
	lManualObject->triangle(0,1,2);
	lManualObject->triangle(2,3,0);
	lManualObject->triangle(4,6,5);
	lManualObject->triangle(6,4,7);
 
	// face top / down
	lManualObject->triangle(0,4,5);
	lManualObject->triangle(5,1,0);
	lManualObject->triangle(2,6,7);
	lManualObject->triangle(7,3,2);
 
	// face left / right
	lManualObject->triangle(0,7,4);
	lManualObject->triangle(7,0,3);
	lManualObject->triangle(1,5,6);
	lManualObject->triangle(6,2,1);			

	lManualObject->end();

	// Here I have finished my ManualObject construction.
	// It is possible to add more begin()-end() thing, in order to use 
	// different rendering operation types, or different materials in 1 ManualObject.
	lManualObject->begin("BaseWhiteNoLighting",Ogre::RenderOperation::OT_LINE_LIST);

	float lAxeSize = 2.0f * lSize;
	lManualObject->position(0.0f, 0.0f, 0.0f);
	lManualObject->colour(Ogre::ColourValue::Red);
	lManualObject->position(lAxeSize, 0.0f, 0.0f);
	lManualObject->colour(Ogre::ColourValue::Red);
	lManualObject->position(0.0f, 0.0f, 0.0f);
	lManualObject->colour(Ogre::ColourValue::Green);
	lManualObject->position(0.0, lAxeSize, 0.0);
	lManualObject->colour(Ogre::ColourValue::Green);
	lManualObject->position(0.0f, 0.0f, 0.0f);
	lManualObject->colour(Ogre::ColourValue::Blue);
	lManualObject->position(0.0, 0.0, lAxeSize);
	lManualObject->colour(Ogre::ColourValue::Blue);
 
	lManualObject->index(0);
	lManualObject->index(1);
	lManualObject->index(2);
	lManualObject->index(3);
	lManualObject->index(4);
	lManualObject->index(5);

	lManualObject->end();

	Ogre::String lNameOfTheMesh = "MeshCubeAndAxe";
	Ogre::String lResourceGroup = Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME;
	lManualObject->convertToMesh(lNameOfTheMesh);
 
	// Now I can create several entities using that mesh
	int lNumberOfEntities = 5;
	for(int iter = 0; iter < lNumberOfEntities; ++iter)
	{
		Ogre::Entity* lEntity = lScene->createEntity(lNameOfTheMesh);
		// Now I attach it to a scenenode, so that it becomes present in the scene.
		Ogre::SceneNode* lNode = lRootSceneNode->createChildSceneNode();
		lNode->attachObject(lEntity);
		// I move the SceneNode so that it is visible to the camera.
		float lPositionOffset = float(1+ iter * 2) - (float(lNumberOfEntities));
		lNode->translate(lPositionOffset, lPositionOffset, -10.0f);
	}

	// I wait until the window is closed.
	// The "message pump" thing is something you will see in most GUI application.
	// It allow the binding of messages between the application and the OS.
	// These messages are most of the time : keystroke, mouse moved, ... or window closed.
	// If I don't do this, the message are never caught, and the window won't close.
	while(!lOgreInit.mWindow->isClosed())
	{
		// Drawings
		// the window update its content.
		// each viewport that is 'autoupdated' will be redrawn now,
		// in order given by its z-order.
		lWindow->update(false);
 
		// The drawn surface is then shown on the screen
		// (google "double buffering" if you want more details).
		// I always use vertical synchro.
		bool lVerticalSynchro = true;
		lWindow->swapBuffers(lVerticalSynchro);
 
		// This update some internal counters and listeners.
		// Each render surface (window/rtt/mrt) that is 'auto-updated' has got its 'update' function called.
		lRoot->renderOneFrame();
 
		Ogre::WindowEventUtilities::messagePump();
	}
	return;
}

int main(int argc, char *argv[])
{
 
	try
	{
		AnOgreApplication();
		std::cout<<"end of the program"<<std::endl;
	}catch(Ogre::Exception &e)
	{
		MWARNING("!!!!Ogre::Exception!!!!\n" << e.what());
	}catch(std::exception &e)
	{
		MWARNING("!!!!std::exception!!!!\n"<<e.what());
	}
	OgreEasy::waitForUser();

	return 0;
}